﻿#include <hacks/base/ev_async.h>

namespace hacks {
    typedef ev_buddy<ev_async> ev_async_buddy;

    ev_async::ev_async(
            const shared_ev_loop &loop,
            ev_async_functor_t &&async_f)
            : ev_async(loop, std::forward<ev_async_functor_t>(async_f), nullptr) {
    }

    ev_async::ev_async(
            const shared_ev_loop &loop,
            ev_async_functor_t &&async_f,
            ev_async_functor_t &&close_f)
            : event_loop_(loop),
              async_(nullptr),
              ev_async_active_functor_(std::forward<ev_async_functor_t>(async_f)),
              ev_async_closed_functor_(std::forward<ev_async_functor_t>(close_f)),
              commit_once_flag_(),
              async_status_(evt_init),
              user_data_(nullptr) {
        assert(event_loop_);
        assert(ev_async_active_functor_);
    }

    ev_async::~ev_async() {
        assert(async_status_ == evt_stopped || async_status_ == evt_commited);
        delete async_;
    }

    void ev_async::commit() {
        std::call_once(commit_once_flag_, [this]() {
            auto ins = shared_from_this();
            assert(ins);
            ins->event_loop_->run_in_loop([ins]() {
                ins->init();
                auto *buddy = new ev_async_buddy;
                buddy->shd_ins = ins;
                ins->async_->data = buddy;
            });
            ins->async_status_.store(evt_commited, std::memory_order_release);
        });
    }

    void ev_async::active() {
        assert(async_status_ == evt_commited);
        event_loop_->run_in_loop([ins = shared_from_this()]() {
            uv_async_send(ins->async_);
        });
    }

    void ev_async::stop_async_task() {
        ev_evt_state exp = evt_commited;
        if (!async_status_.compare_exchange_strong(exp, evt_stopping, std::memory_order_acq_rel)) {
            return;
        }
        event_loop_->run_in_loop([ins = shared_from_this()]() {
            uv_close(reinterpret_cast<uv_handle_t *>(ins->async_), &ev_async::uv_async_close_callback);
        });
    }

    void ev_async::init() {
        async_ = new uv_async_t;
        uv_async_init(event_loop_->get_raw_loop(), async_, &ev_async::uv_async_callback);
    }

    void ev_async::uv_async_callback(uv_async_t *async) {
        ev_async_buddy *buddy = static_cast<ev_async_buddy *>(async->data);
        buddy->shd_ins->async_actived();
    }

    void ev_async::async_actived() {
        if (ev_async_active_functor_) {
            ev_async_active_functor_(*this);
        }
    }

    void ev_async::uv_async_close_callback(uv_handle_t *h) {
        ev_async_buddy *buddy = static_cast<ev_async_buddy *>(h->data);
        buddy->shd_ins->async_status_.store(evt_stopped);
        buddy->shd_ins->async_closed();
        delete buddy;
    }

    void ev_async::async_closed() {
        if (ev_async_closed_functor_) {
            ev_async_closed_functor_(*this);
        }
    }

    void *ev_async::get_userdata() const {
        return user_data_;
    }

    void ev_async::set_userdata(void *udata) {
        user_data_ = udata;
    }
}
